<script setup lang="ts">
import { testapi } from "@/api";
import HelloWorld from './components/HelloWorld.vue'
import { provide, reactive } from 'vue'
import { provideKey, provideKey2, ArrItem } from './types'
// env相关练习 ===============================
console.log("import.meta:", import.meta)
// console.log('import.meta.env:',import.meta.env)
// console.log('import.meta.hot:',import.meta.hot)
console.log('NODE_ENV:', process.env.NODE_ENV)
// console.log(import.meta.env.VITE_APP_BASE_API)
import imgUrl from '/vite.svg'
// import { basename } from 'path/posix'
// console.log("imgUrl:", imgUrl)
// console.log("import.meta.url:", import.meta.url)
let filename = 'vue'
const imgUrl2 = new URL(`@/assets/vue.svg`, import.meta.url).href
// 注意别名@的时候不能用字符串变量
const imgUrl3 = new URL(`./assets/${filename}.svg`, import.meta.url).href
/*
在生产构建时，Vite 才会进行必要的转换保证 URL 在打包和资源哈希后仍指向正确的地址。
然而，这个 URL 字符串必须是静态的，这样才好分析。否则代码将被原样保留、因而在 build.target 不支持 import.meta.url 时会导致运行时错误。
*/
const imgUrl4 = new URL(imgUrl, import.meta.url).href
console.log("imgUrl2:", imgUrl2)
console.log("imgUrl3:", imgUrl3)
console.log("imgUrl4:", imgUrl4)
// console.log('import.meta.env.BASE_URL:', import.meta.env.BASE_URL)
console.log("VITE_PUBLIC:", import.meta.env.VITE_PUBLIC)
// 为provide提供强校验 =============================
provide(provideKey, 'foo2') // 若提供的是非字符串值会导致错误
provide(provideKey2, {name: 'jack', gender: 'male',obj:{name:'jack',age:18, arr: [{name:'12', age:1, intro:'hello'}]}})
// 用函数的第一个参数的泛型约束第二个参数的类型
// 一下两种写法都可以
// const test_func: {<Type>(p: Array<Type> | Type, val:Type):void} = test // 对象字面量类型写法
const test_func: <Type>(p: Array<Type> | Type, val:Type)=>void = test // 函数类型表达式写法
function test<Type>(p: Array<Type> | Type, val:Type):void {
  if (Array.isArray(p) ? typeof p[0] !== typeof val : typeof p !== typeof val) {
    console.log('test - 不通过：类型不一致', typeof p[0] ?? typeof p, typeof val);
  } else {
    console.log('test - 通过：类型一致', typeof p[0] ?? typeof p, typeof val);
  }
}
test_func([1,2], 1) // 或者 test([1,2], 1)
test('string', 1) // 错误: 函数的两个参数类型不一致
function test2<Type>(p: Array<Type> | string, val:Type):void {
  // 判断传参是否符合规范
  if (Array.isArray(p) && typeof p[0] !== typeof val) {
    console.log('test2 - 不通过：类型不一致', typeof p[0] ?? typeof p, typeof val);
  } else {
    console.log('test2 - 通过：不要求类型一致');
  }
}
test2('string', 1) // 通过
test2(['s'], 1) // 不通过
// 泛型类型:
// 函数表达式类型
// 泛型函数1  与函数声明结合：
let myIdentity: <Type>(arg: Type) => Type = identity;
function identity(name) { // 这样写也行
// function identity<Type>(name:  Type): Type { // 这样写也行,跟下面的写法一样
  console.log('name:',name)
  return name
}
myIdentity('jack')
// 泛型函数2 直接函数表达式：
let myAge:<Type>(age:Type) => Type = function (age){
  console.log('age:',age)
  return age
}
myAge(15)
// 泛型函数3 直接函数表达式 + 对象字面量类型：
let myAge2:{<Type>(age:Type): Type} = function (age){
  console.log("myAge2:",20)
  return age
}
myAge2(20)

// 泛型函数4 直接函数表达式 + 接口类型：
interface MyAge<Type>{
  (age:Type):Type
  age?: number
}
let myAge3:MyAge<Number> = function (age){
  console.log("myAge3:",30)
  return age
}
myAge3(30)
// 函数声明式类型 - 常用写法
function myName<Type>(name: Type):Type{
  console.log("my name:", name)
  return name
}
myName('Ray')



// review ============================================
interface testing<Type> {
  name: Type
  age?: number
}
function tt<Type>(a:testing<Type> | string, b:Type):void{
  console.log("a:", a)
  console.log("b:", b)
}
// 泛型函数调用
let aa: testing<number> = {
  name: 2
}
tt<number>(aa, 1) // number类型

let bb: testing<string> = {
  name: '1'
}
tt<string>(bb, '1') // string类型
function dd<Type>(a: testing<Type>, b?: Type) {
  console.log("dd:", a, b || '-')
}
dd(aa,1)
// 泛型函数表达式
interface ma<Type>{
  name: Type
}
let myFunc:<Type>(a: ma<Type> | string, b: Type) => void = (a, b) => {
  console.log(a, b)
}
let my_a: ma<number> = {
  name: 1
}
myFunc(my_a, 2)
myFunc(1, '2') // 第一个参数只能是字符串或者ma<Type>类型

interface Book<Type>{
  name: string
  age: number
  gender?: string,
  intro: Type
}
let mTest:Book<number> = reactive({age:1, name:'zz', intro: 1})
console.log("mTest:", mTest)
let ss:ArrItem<string> = reactive({
    name: 'string',
    age: 1,
    intro: 'Type'
})
console.log(ss)
// array.push 返回当前数组的长度
let aa_t = [1,2,3,4,5,8,2]
// let aa_l = aa_t.push(...[1,2,3])
// aa_t.slice(0,2)
console.log("slice: ",aa_t.slice(1,4)) // 用于提取数组的一部分，并返回一个新数组，不会修改原数组。语法：array.slice(startIndex, endIndex)，不包括endIndex
// aa_t.splice(0,1)
console.log("splice: ",aa_t.splice(1,4), aa_t) // 用于添加或删除数组中的元素，并返回被删除的元素组成的数组。会修改原数组。语法：array.splice(startIndex, deleteCount, item1, ...)
// 元组 剩余形参
// 剩余参数
type StringNumberPair<Type> = [number | string, Type, boolean?];
type StringNumberPair2<Type> = [number, Type, ...boolean[]];
let sss:StringNumberPair<string> = [1,'4']
let sss2:StringNumberPair2<string> = [1,'4',true,false]
let sss3:StringNumberPair2<number> = [1,4,true,false]
function tupleFunc<Type>(...arg:StringNumberPair2<Type>) {
  const [a,b,...bs] = arg
  console.log("bs:",bs)
  // const []
  console.log("...bs:",...bs)
  console.log("...arg:", ...arg)
  console.log("a, b:", a, b)
}
tupleFunc(...sss2)
tupleFunc(...sss3)
tupleFunc(1,'s')
let arr_rr = [1,2,3]
console.log("...arr_rr :", ...arr_rr)
console.log("arr_rr :",arr_rr)
function fff(...args: StringNumberPair<string>) {
  const [a,b] = args
  console.log(a, b)
}
fff(1,'s', true)
// 函数重载 ====
// 函数重载注意点：参数与返回值匹配，两个及以上的重载签名（尽可能使用联合类型的参数而不是重载）
function fn(x: string): string; // 重载签名
function fn(x: number, y: string, z?: string): string; // 同上
function fn(x: string | number, y?: string, z?: string) { // 实现签名
  if(y) {
    if(z) {
      return x + y + z;
    }
    return x + y;
  }
  return x+'';
}
fn('1') // ok
fn(1, '1') // ok
// fn(1) // not ok

// const 断言
function becomeAdmin() {
  console.log(this)
}
becomeAdmin()
const args = [8, 5] as const;
const args2 = <const>[8, 5]; // 断言 const类型，等于 readonlyArray<number> , readonly number[]
// 类====================
// 继承
class Base {
  k = 4;
}

class Derived extends Base {
  constructor(age) {
    // Prints a wrong value in ES5; throws exception in ES6
    super();
    console.log(this.k);
// 'super' must be called before accessing 'this' in the constructor of a derived class.
    // super();
  }
}
let Derived_o = new Derived(1)
// 使用类类型，通过构造函数引用类类型（工厂函数）
class C2 {
  z = 1
}
class C extends C2 {
  x = 10;
  y = 3;
}
function factory_class<Type extends C2, Type2>(c: {new (Type2): Type}, age: Type2): Type{
  return new c(age)
}
// let myAge2:{<Type>(age:Type): Type} = function (age){
//   console.log("myAge2:",20)
//   return age
// }
console.log(factory_class(C, '22'))
let cc = new C()
console.log(typeof factory_class(C, 23))

async function testFunc() {
  const res = await testapi({
    name: 'test',
    age: 12
  })
  console.log(res)
}
testFunc()
</script>

<template>
  <div>
    <!-- <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a> -->
  </div>
  <HelloWorld msg="Vite + Vue" />
</template>

<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
